package thread;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * ReentrantLock的后续代码例子。
 * 6：多个条件变量：lock支持多个条件变量。
 *      使用要点：
 *          1：【await前需要获得锁】。
 *          2：【await执行后，会释放锁，进入conditionObject等待】
 *          3：await 的线程被唤醒（或打断、或超时）去重新竞争 lock 锁
 *          4：竞争 lock 锁成功后，从 await 后继续执行
 *      小结：就是一个lock可以生成多个【Condition条件变量】。之后可以调用【condition#await方法】暂停线程（不过必须要先获取到这个锁才行）。
 *          调用【condition#signal方法】释放这个条件变量。
 *          1：调用【await】方法前，需要获取锁。
 *          2：调用【await】方法后，会暂停这个线程，并释放锁。
 *          3：【await暂停的线程】，可以【被唤醒】、【打断】、【超时】，去重新竞争lock锁。
 *          4：竞争lock锁成功之后，从【await】方法后继续执行。
 *
 * 7：
 */
public class LockTest2 {

    /**
     * lock的多个条件变量。
     */

    //锁
    static ReentrantLock lock = new ReentrantLock();
    //条件变量，一个锁可以有多个条件变量
    static Condition waitCigaretteQueue = lock.newCondition();
    static Condition waitbreakfastQueue = lock.newCondition();

    static volatile boolean hasCigrette = false;
    static volatile boolean hasBreakfast = false;

    public static void main(String[] args) throws InterruptedException {

        //等待 waitCigaretteQueue 的条件变量。
        new Thread(() -> {
            try {
                lock.lock();
                while (!hasCigrette) {
                    try {
                        waitCigaretteQueue.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("等到了它的烟");
            } finally {
                lock.unlock();
            }
        }).start();

        //等待 waitbreakfastQueue 的条件变量。
        new Thread(() -> {
            try {
                lock.lock();
                while (!hasBreakfast) {
                    try {
                        waitbreakfastQueue.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("等到了它的早餐");
            } finally {
                lock.unlock();
            }
        }).start();

        Thread.sleep(1000);
        sendBreakfast();
        Thread.sleep(1000);
        sendCigarette();
    }

    //释放 waitCigaretteQueue 的条件变量。
    private static void sendCigarette() {
        lock.lock();
        try {
            System.out.println("送烟来了");
            hasCigrette = true;
            waitCigaretteQueue.signal();
        } finally {
            lock.unlock();
        }
    }

    //释放 waitCigaretteQueue 的条件变量。
    private static void sendBreakfast() {
        //只有获取了这个锁的线程，才能调用【signal】方法。
        lock.lock();
        try {
            System.out.println("送早餐来了");
            hasBreakfast = true;
            waitCigaretteQueue.signal();
        } finally {
            lock.unlock();
        }
    }
}
